using System;
using System.Collections;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Drawing;
using System.Windows.Forms;
using RideMe.AddIns.Interfaces;

namespace RideMe.AddIns.Events
{
    /// <summary>
    /// This defines user interface events that an add-in can subscribe to to ensure that it is kept up to date about 
    /// UI events and updates
    /// </summary>
    public class UserInterfaceEventSource
    {
        #region Events

        public event EventHandler<MenuCreatedEventArgs> MenuCreated;
        public event EventHandler<ControlCreatedEventArgs> OptionsControlCreated;
        public event EventHandler<DockControlCreatedEventArgs> LeftDockControlCreated;
        public event EventHandler<DockControlCreatedEventArgs> RightDockControlCreated;
        public event EventHandler<DockControlCreatedEventArgs> BottomDockControlCreated;
        public event EventHandler<ProjectExplorerContextMenuCreatedEventArgs> ProjectExplorerContextMenuCreated;
        public event EventHandler<ControlCreatedEventArgs> OutputWindowControlCreated;

        #endregion

        #region Event Methods

        /// <summary>
        /// This is called to raise the event that the menu control has now been created
        /// </summary>
        /// <param name="e"></param>
        public void OnMenuCreated(MenuCreatedEventArgs e)
        {
            if (MenuCreated != null)
                MenuCreated(this, e);
        }

        /// <summary>
        /// This is called to raise the event that the options control has now been created, allowing a single add-in to render the relevant options control
        /// </summary>
        /// <param name="addin"></param>
        /// <param name="e"></param>
        public void OnOptionsControlCreated(IAddIn addin, ControlCreatedEventArgs e)
        {
            if (OptionsControlCreated != null)
            {
                foreach (EventHandler<ControlCreatedEventArgs> handler in OptionsControlCreated.GetInvocationList())
                {
                    if (handler.Target is IAddIn && ((IAddIn)handler.Target) == addin)
                    {
                        handler.Invoke(this, e);
                    }
                }
            }
        }

        /// <summary>
        /// This is called to raise the event indicating that the left dock control has now been created
        /// </summary>
        /// <param name="e"></param>
        public void OnLeftDockControlCreated(DockControlCreatedEventArgs e)
        {
            if (LeftDockControlCreated != null)
                LeftDockControlCreated(this, e);
        }

        /// <summary>
        /// This is called to raise the event indicating that the right dock control has now been created
        /// </summary>
        /// <param name="e"></param>
        public void OnRightDockControlCreated(DockControlCreatedEventArgs e)
        {
            if (RightDockControlCreated != null)
                RightDockControlCreated(this, e);
        }

        /// <summary>
        /// This is called to raise the event indicating that the bottom dock control has now been created
        /// </summary>
        /// <param name="e"></param>
        public void OnBottomDockControlCreated(DockControlCreatedEventArgs e)
        {
            if (BottomDockControlCreated != null)
                BottomDockControlCreated(this, e);
        }

        /// <summary>
        /// This is called to raise the event indicating that the project explorer context menu has been created
        /// </summary>
        /// <param name="e"></param>
        public void OnProjectExplorerContextMenuCreated(ProjectExplorerContextMenuCreatedEventArgs e)
        {
            if (ProjectExplorerContextMenuCreated != null)
                ProjectExplorerContextMenuCreated(this, e);
        }

        /// <summary>
        /// This is called to raise the output window control created event to subscribers
        /// </summary>
        /// <param name="e"></param>
        public void OnOutputWindowControlCreated(ControlCreatedEventArgs e)
        {
            if (OutputWindowControlCreated != null)
                OutputWindowControlCreated(this, e);
        }

        #endregion

        #region Event Args

        /// <summary>
        /// These event args are used when the menu is created in RIDE-ME
        /// </summary>
        public class MenuCreatedEventArgs : EventArgs
        {
            #region Constructor

            /// <summary>
            /// This initiates the menu creation event args from a tool strip control
            /// </summary>
            /// <param name="menu"></param>
            public MenuCreatedEventArgs(ToolStrip menu)
                : base()
            {
                _menu = menu;
            }

            #endregion

            #region Private Variables

            private ToolStrip _menu;

            #endregion

            #region Methods

            /// <summary>
            /// This adds a menu item
            /// </summary>
            /// <param name="menuItem"></param>
            public void AddMenuItem(ToolStripMenuItem menuItem)
            {
                //This inserts the menu item before the Help menu at the end
                _menu.Items.Insert(_menu.Items.Count - 1, menuItem);
            }

            /// <summary>
            /// This adds a menu item to an existing top-level item specified by name
            /// </summary>
            /// <param name="name"></param>
            /// <param name="menuItem"></param>
            public void AddMenuItem(string name, ToolStripMenuItem menuItem)
            {
                //Loop through all items
                foreach (ToolStripMenuItem item in _menu.Items)
                {
                    //Find one with a matching name (removing the & character used for hot keys)
                    if (item.Text.Replace("&", string.Empty) == name)
                    {
                        //Add it to the dropdown items, then return to exit out now that the task is complete
                        item.DropDownItems.Add(menuItem);
                        return;
                    }
                }
            }

            #endregion
        }

        /// <summary>
        /// This is raised when a control is created, and is used to collection child controls to populate it with
        /// </summary>
        public class ControlCreatedEventArgs : EventArgs
        {
            #region Constructor

            /// <summary>
            /// This creates event arguments from a control
            /// </summary>
            /// <param name="control"></param>
            public ControlCreatedEventArgs(Control control)
                : base()
            {
                _control = control;
            }

            #endregion

            #region Private Variables

            private Control _control;

            #endregion

            #region Properties

            /// <summary>
            /// This returns the control created
            /// </summary>
            public Control Control
            {
                get { return _control; }
            }

            #endregion

            #region Methods

            /// <summary>
            /// This adds a child control to the parent
            /// </summary>
            /// <param name="child"></param>
            public void AddControl(Control child)
            {
                _control.Controls.Add(child);
            }

            /// <summary>
            /// This removes a child control from the parent
            /// </summary>
            /// <param name="child"></param>
            public void RemoveControl(Control child)
            {
                _control.Controls.Remove(child);
            }

            #endregion
        }

        /// <summary>
        /// This is raised when a dock control is created
        /// </summary>
        public class DockControlCreatedEventArgs : EventArgs
        {
            #region Constructor

            /// <summary>
            /// This creates event arguments from a docked container control
            /// </summary>
            /// <param name="control"></param>
            public DockControlCreatedEventArgs(TD.SandDock.DockContainer control)
                : base()
            {
                _control = control;
            }

            #endregion

            #region Private Variables

            private TD.SandDock.DockContainer _control;

            #endregion

            #region Methods

            /// <summary>
            /// This adds a control as a docked control to the dock container
            /// </summary>
            /// <param name="child"></param>
            public void AddControl(Control child)
            {
                AddControl(child, RideMe.AddIns.Properties.Resources.ActualSizeHS, true, null, null);
            }

            /// <summary>
            /// This adds a control as a docked control to the dock container, and specifies a delegate for when the control is closed
            /// </summary>
            /// <param name="child"></param>
            /// <param name="onClose"></param>
            public void AddControl(Control child, EventHandler onClose)
            {
                AddControl(child, RideMe.AddIns.Properties.Resources.ActualSizeHS, true, onClose, null);
            }

            /// <summary>
            /// This adds a control as a docked control the dock container, and specifies a delegate for when the control is closed
            /// </summary>
            /// <param name="child"></param>
            /// <param name="onClosing"></param>
            public void AddControl(Control child, CancelEventHandler onClosing)
            {
                AddControl(child, RideMe.AddIns.Properties.Resources.ActualSizeHS, true, null, onClosing);
            }

            /// <summary>
            /// This adds a control as a docked control to the dock container, with the specified image, also specifying whether it is to become the selected
            /// control within its parent dock container, and also allowing delegates to be specified that'll be fired when the control is closed
            /// </summary>
            /// <param name="child"></param>
            /// <param name="image"></param>
            /// <param name="selected"></param>
            /// <param name="onClose"></param>
            /// <param name="onClosing"></param>
            public void AddControl(Control child, Image image, bool selected, EventHandler onClose, CancelEventHandler onClosing)
            {
                //Build the dock control around the child control
                TD.SandDock.DockControl dock = new TD.SandDock.DockControl(
                            child,
                            child.Name);
                if (onClose != null)
                    dock.Closed += new EventHandler(onClose);
                if(onClosing!=null)
                    dock.Closing += new CancelEventHandler(onClosing);
                dock.TabImage = image;
                //Add the dock control
                AddDockControl(dock, selected);
            }

            /// <summary>
            /// This removes a child control from the parent
            /// </summary>
            /// <param name="child"></param>
            public void RemoveControl(Control child)
            {
                //Grab the control system
                TD.SandDock.ControlLayoutSystem layoutSystem = GetControlLayoutSystem();
                //Loop through all dock controls
                for (int i = 0; i < layoutSystem.Controls.Count; i++)
                {
                    //Loop through all child controls on the dock control
                    for (int j = 0; j < layoutSystem.Controls[i].Controls.Count; j++)
                    {
                        //If the child control is equal to the specified child control, then we know the parent dock control is the one to remove
                        if (layoutSystem.Controls[i].Controls[j] == child)
                        {
                            //Remove the control
                            layoutSystem.Controls[i].Controls.Remove(layoutSystem.Controls[i].Controls[j]);
                            //Hide the dock control
                            layoutSystem.Controls[i].Hide();
                            //Remove the dock control
                            RemoveDockControl(layoutSystem.Controls[i]);
                            //Select the first control if there is still one left
                            if (layoutSystem.Controls.Count > 0)
                            {
                                layoutSystem.SelectedControl = layoutSystem.Controls[0];
                                //Hide that dock control so that we ensure the dock control is refreshed
                                layoutSystem.SelectedControl.Hide();
                            }
                            //Return
                            return;
                        }
                    }
                }
            }

            /// <summary>
            /// This shows the specified control, making it the selected control within the dock container
            /// </summary>
            /// <param name="child"></param>
            public void ShowControl(Control child)
            {
                //Grab the control system
                TD.SandDock.ControlLayoutSystem layoutSystem = GetControlLayoutSystem();
                //Loop through all dock controls
                for (int i = 0; i < layoutSystem.Controls.Count; i++)
                {
                    //Loop through all child controls on the dock control
                    for (int j = 0; j < layoutSystem.Controls[i].Controls.Count; j++)
                    {
                        //If the child control is equal to the specified child control, then we know the parent dock control is the one to select
                        if (layoutSystem.Controls[i].Controls[j] == child)
                        {
                            //Set the selected dock control to the parent dock control of the child
                            layoutSystem.SelectedControl = layoutSystem.Controls[i];
                            //Open up the dock control, in case it was hidden
                            layoutSystem.SelectedControl.Open();
                            //Set the focus to it
                            layoutSystem.SelectedControl.Focus();
                            break;
                        }
                    }
                }
            }

            /// <summary>
            /// This returns the control layout system in use for the dock container
            /// </summary>
            /// <returns></returns>
            private TD.SandDock.ControlLayoutSystem GetControlLayoutSystem()
            {
                //This will hold the layout system for the dock container
                TD.SandDock.ControlLayoutSystem layoutSystem = null;
                //Loop through all the layout systems looking for the control layout system
                for (int i = 0; i < _control.LayoutSystem.LayoutSystems.Count; i++)
                {
                    if (_control.LayoutSystem.LayoutSystems[i] is TD.SandDock.ControlLayoutSystem)
                        layoutSystem = (TD.SandDock.ControlLayoutSystem)_control.LayoutSystem.LayoutSystems[i];
                }
                //If we couldn't find one, then maybe there isn't one and we should instead create and add one
                if (layoutSystem == null)
                {
                    layoutSystem = new TD.SandDock.ControlLayoutSystem(_control.Width, _control.Height, new TD.SandDock.DockControl[0], null);
                    _control.LayoutSystem.LayoutSystems.Add(layoutSystem);
                }
                //Return our control layout system that was found
                return layoutSystem;
            }

            /// <summary>
            /// This adds the specified dock control
            /// </summary>
            /// <param name="dockControl"></param>
            /// <param name="selected"></param>
            private void AddDockControl(TD.SandDock.DockControl dockControl, bool selected)
            {
                //Grab the layout system
                TD.SandDock.ControlLayoutSystem layoutSystem = GetControlLayoutSystem();
                //Add the dock control
                layoutSystem.Controls.Add(dockControl);
                //Select it if required
                if (selected)
                {
                    layoutSystem.SelectedControl = layoutSystem.Controls[0];
                    layoutSystem.SelectedControl.Open();
                    layoutSystem.SelectedControl.Focus();
                }
                //Make the container visible, in case the container was hidden because of a lack of controls
                if (layoutSystem.Controls.Count > 0)
                    layoutSystem.DockContainer.Visible = true;
            }

            /// <summary>
            /// This removes the specified dock control
            /// </summary>
            /// <param name="dockControl"></param>
            private void RemoveDockControl(TD.SandDock.DockControl dockControl)
            {
                //Grab the layout system
                TD.SandDock.ControlLayoutSystem layoutSystem = GetControlLayoutSystem();
                //Remove the dock control
                layoutSystem.Controls.Remove(dockControl);
                //If this leaves the container system with no controls, then hide the container
                if (layoutSystem.Controls.Count == 0)
                    layoutSystem.DockContainer.Visible = false;
            }

            #endregion
        }

        /// <summary>
        /// This is raised whenever the project explorer context menu is created, i.e. a right-click to the explorer control
        /// </summary>
        public class ProjectExplorerContextMenuCreatedEventArgs : EventArgs
        {
            #region Constructor

            /// <summary>
            /// This creates the event args against the specified menu strip, with the specified path being selected on the project explorer
            /// </summary>
            /// <param name="menu"></param>
            /// <param name="path"></param>
            public ProjectExplorerContextMenuCreatedEventArgs(ContextMenuStrip menu, string path)
            {
                _menu = menu;
                _path = path;
            }

            #endregion

            #region Private Variables

            private ContextMenuStrip _menu;
            private string _path;

            #endregion

            #region Properties

            /// <summary>
            /// This returns the path of the selected node within the project explorer
            /// </summary>
            public string Path
            {
                get { return _path; }
            }

            #endregion

            #region Methods

            /// <summary>
            /// This adds the specified menu item
            /// </summary>
            /// <param name="menuItem"></param>
            public void AddMenuItem(ToolStripItem menuItem)
            {
                _menu.Items.Add(menuItem);
            }

            #endregion
        }

        #endregion
    }
}
